package handlers

import (
	"crypto/rand"
	"fmt"
	"io/ioutil"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "../lib/log"
)

// 文件上传最大值
const maxUploadSize = 5 * 1024 * 2014 // 5 MB

const uploadPath = "./files"
const urlPath = "/files"

// 允许的文件类型
var allowedFileTypes = []string{
	"image/jpeg",
	"image/gif",
	"image/jpg",
	"image/png",
	"application/pdf",
}

// UploadFileInit 启动文件上传服务
func UploadFileInit() {

	// 文件上传接口（非jsonrpc）
	http.HandleFunc("/upload", uploadFileHandler)

	// 获取文件
	http.HandleFunc(urlPath+"/", getFile)

	// 创建静态资源文件夹
	if createFileDir(uploadPath) != nil {
		log.Fatal("Unable to create dir of files")
	} else {
		log.Success("Created dir for static files")
	}

}

func pathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}

	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func createFileDir(dir string) error {
	exist, err := pathExists(uploadPath)
	if err != nil {
		return err
	}
	if !exist {
		// 创建文件夹
		err = os.Mkdir(dir, os.ModePerm)
		if err != nil {
			return err
		}
	}
	return nil
}

func getFile(w http.ResponseWriter, r *http.Request) {
	filename := strings.TrimPrefix(r.URL.Path, urlPath+"/")
	b, err := ioutil.ReadFile(uploadPath + "/" + filename)
	if err != nil {
		renderError(w, "NOT_FOUND", http.StatusNotFound)
		return
	}
	w.Write(b)
}

/**
*
* @api {post} /upload 1. 上传文件
* @apiVersion 1.0.0
* @apiGroup File
* @apiDescription
* 上传文件，前端请使用 Content-Type:multipart/form-data; 上传成功状态为200且返回文件相对路径；
*
* @apiParam {file} file 要上传的文件
* @apiParamExample {json} 请求示例
curl -F "file=@me.jpg" http://SERVERHOST/upload

* @apiSuccess ... {string} 文件url的相对路径
* @apiSuccessExample {json} 返回示例
files/6c75f2a459d7f761440a0d0a.jpg
*
* @apiError 400 FILE_TOO_BIG
* @apiError 400 INVALID_FILE
* @apiError 400 INVALID_FILE_TYPE
* @apiError 400 CANT_WRITE_FILE
*/
func uploadFileHandler(w http.ResponseWriter, r *http.Request) {

	//验证文件大小
	r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
	if err := r.ParseMultipartForm(maxUploadSize); err != nil {
		renderError(w, "FILE_TOO_BIG", http.StatusBadRequest)
		return
	}

	file, _, err := r.FormFile("file")

	if err != nil {
		renderError(w, "INVALID_FILE", http.StatusBadRequest)
		log.Error(err)
		return
	}
	defer file.Close()
	fileBytes, err := ioutil.ReadAll(file)
	if err != nil {
		renderError(w, "INVALID_FILE", http.StatusBadRequest)
		log.Error(err)
		return
	}

	// 验证文件类型
	filetype := http.DetectContentType(fileBytes)
	legal := false
	for i := range allowedFileTypes {
		if filetype == allowedFileTypes[i] {
			legal = true
			break
		}
	}
	if !legal {
		renderError(w, "INVALID_FILE_TYPE", http.StatusBadRequest)
		return
	}

	// 保存临时文件
	fileName := randToken(12)
	fileEndings, err := mime.ExtensionsByType(filetype)
	if err != nil {
		renderError(w, "CANT_READ_FILE_TYPE", http.StatusInternalServerError)
		return
	}
	newPath := filepath.Join(uploadPath, fileName+fileEndings[0])
	log.Debugf("File uploaded, FileType: %s, File: %s", filetype, newPath)

	//
	newFile, err := os.Create(newPath)
	if err != nil {
		renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError)
		return
	}
	defer newFile.Close()
	if _, err := newFile.Write(fileBytes); err != nil {
		renderError(w, "CANT_WRITE_FILE", http.StatusInternalServerError)
		return
	}
	w.Write([]byte("/" + newPath))
}

func renderError(w http.ResponseWriter, message string, statusCode int) {
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte(message))
}

func randToken(len int) string {
	b := make([]byte, len)
	rand.Read(b)
	t := time.Now()
	format := t.Format(time.RFC3339)
	return fmt.Sprintf("%v-%x", format, b)
}
